go to index

Loops and List Comprehensions

read time 9 min read
Kaggle Python

Loops and List Comprehensions

Loops

**Loops(循环)**是重复执行某些代码的一种方式。这里有一个例子:

python
planets = ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']
for planet in planets:
    print(planet, end=' ') # print all on same line
plaintext
Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune 

for循环规定

  • 要使用的变量名(在本例中为planet)。
  • 要遍历的值集合(在本例中是planets)。

你用”in”这个词把它们连在一起。

in 右边的对象可以是任何支持迭代的对象。基本上,如果它可以被认为是一组东西,你可能可以循环遍历它。除了列表,还可以遍历元组中的元素:

python
multiplicands = (2, 2, 2, 3, 3, 5)
product = 1
for mult in multiplicands:
    product = product * mult
product
plaintext
360

你甚至可以循环遍历字符串中的每个字符:

python
s = 'steganograpHy is the practicE of conceaLing a file, message, image, or video within another fiLe, message, image, Or video.'
msg = ''
# print all the uppercase letters in s, one at a time
for char in s:
    if char.isupper():
        print(char, end='')        
plaintext
HELLO
range()

range() 是一个返回数字序列的函数。事实证明,它在编写循环时非常有用。

例如,如果我们想将某个操作重复5次:

python
for i in range(5):
    print("Doing important work. i =", i)
plaintext
Doing important work. i = 0
Doing important work. i = 1
Doing important work. i = 2
Doing important work. i = 3
Doing important work. i = 4
while loops

Python中的另一种循环是while循环,它不断迭代,直到满足某个条件:

python
i = 0
while i < 10:
    print(i, end=' ')
    i += 1 # increase the value of i by 1
plaintext
0 1 2 3 4 5 6 7 8 9 

while循环的参数被计算为一个布尔语句,循环一直执行,直到该语句的计算结果为False。

List comprehensions

**List comprehensions(列表推导式)**是Python最受欢迎和最独特的特性之一。理解它们的最简单方法可能是看几个例子:

python
squares = [n**2 for n in range(10)]
squares
plaintext
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Here’s how we would do the same thing without a list comprehension:

python
squares = []
for n in range(10):
    squares.append(n**2)
squares
plaintext
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

我们还可以添加一个if条件:

python
short_planets = [planet for planet in planets if len(planet) < 6]
short_planets
plaintext
['Venus', 'Earth', 'Mars']

(如果你熟悉SQL,可能会认为这类似于“WHERE”子句)

下面是一个使用if条件进行筛选并对循环变量进行某种变换的例子:

python
# str.upper() returns an all-caps version of a string
loud_short_planets = [planet.upper() + '!' for planet in planets if len(planet) < 6]
loud_short_planets
plaintext
['VENUS!', 'EARTH!', 'MARS!']

人们通常将这些代码写在一行中,但如果将它们分成3行,你可能会发现结构更清晰:

python
[
    planet.upper() + '!' 
    for planet in planets 
    if len(planet) < 6
]
plaintext
['VENUS!', 'EARTH!', 'MARS!']

(继续用SQL做类比,可以把这三行想象成SELECT、FROM和WHERE。)

从技术上讲,左边的表达式不必涉及循环变量(尽管不涉及循环变量的情况很少见)。你认为下面的表达式会求值为什么?按下output按钮进行检查。

python
[32 for planet in planets]
plaintext
[32, 32, 32, 32, 32, 32, 32, 32]

将列表推导式与min、max和sum等函数结合在一起,可以让人印象深刻地用一行代码解决原本需要几行代码才能解决的问题。

例如,比较下面两个做相同事情的代码单元格。

python
def count_negatives(nums):
    """Return the number of negative numbers in the given list.
    
    >>> count_negatives([5, -1, -2, 0, 3])
    2
    """
    n_negative = 0
    for num in nums:
        if num < 0:
            n_negative = n_negative + 1
    return n_negative

下面是一个使用列表推导式的解决方案:

python
def count_negatives(nums):
    return len([num for num in nums if num < 0])

好多了,对吧?

好吧,如果我们只关心最小化代码的长度,那么第三种解决方案会更好!

python
def count_negatives(nums):
    # Reminder: in the "booleans and conditionals" exercises, we learned about a quirk of 
    # Python where it calculates something like True + True + False + True to be equal to 3.
    return sum([num < 0 for num in nums])

这些解决方案中哪一个是“最好的”完全是主观的。用更少的代码解决问题总是好的,但值得记住以下几行Python之禅:

可读性。 显式比隐式好。

因此,使用这些工具来编写紧凑可读的程序。但是当你不得不选择的时候,选择别人容易理解的代码。

Exercise

Question 1

你是否觉得调试过程中需要一点运气?下面的程序有一个bug。试着找出bug并修复它。

Error Code:

python
def has_lucky_number(nums):
    """Return whether the given list of numbers is lucky. A lucky list contains
    at least one number divisible by 7.
    """
    for num in nums:
        if num % 7 == 0:
            return True
        else:
            return False

Correct Code:

python
def has_lucky_number(nums):
    """Return whether the given list of numbers is lucky. A lucky list contains
    at least one number divisible by 7.
    """
    for num in nums:
        if num % 7 == 0:
            return True
    else:
        return False
    

# Check your answer
q1.check()

Question 2

请看下面的Python表达式。你觉得我们运行后会得到什么?完成预测后,取消代码注释并运行单元格以查看是否正确。

python
[1, 2, 3, 4] > 2
python
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[6], line 1
----> 1 [1, 2, 3, 4] > 2

TypeError: '>' not supported between instances of 'list' and 'int'

R和Python有一些库(如numpy和pandas)将列表中的每个元素与2进行比较(即进行“元素级”比较),并给我们一个布尔值列表,如[False, False, True, True]。

实现一个函数来重现这种行为,返回一个布尔值列表,对应于对应元素是否大于n。

python
def elementwise_greater_than(L, thresh):
    """Return a list with the same length as L, where the value at index i is 
    True if L[i] is greater than thresh, and False otherwise.
    
    >>> elementwise_greater_than([1, 2, 3, 4], 2)
    [False, False, True, True]
    """
    pass
    for num in L:
        if num > thresh:
            L[L.index(num)] = True
        else :
            L[L.index(num)] = False
    return L
# Check your answer
q2.check()

Question 3

根据函数的文档字符串完成下面的函数体。

plaintext
def menu_is_boring(meals):
    """Given a list of meals served over some period of time, return True if the
    same meal has ever been served two days in a row, and False otherwise.
    """
    pass
    for i in range(len(meals) - 1):  # 遍历列表,注意不要越界
        if meals[i] == meals[i + 1]:
            return True  # 如果发现连续相同,返回True
    return False  # 如果没有发现连续相同,返回False
    
# Check your answer
q3.check()

Question 4

在21点赌桌旁边,Python Challenge赌场有一台老虎机。调用play_slot_machine()可以得到老虎机的运行结果。它返回的数字是你的美元奖金。通常它返回0。但有时你会很幸运,得到一大笔钱。试着运行下面的代码:

顺便说一下,我们有没有提过,每次摇动老虎机的价格是1美元?别担心,我们稍后会把帐单寄给你。

平均来说,每次玩这台机器,你期望获得(或失去)多少钱?赌场对此保密,但你可以用蒙特卡洛方法(Monte Carlo method)估计每次拉牌的平均值。为了估计平均结果,我们多次模拟场景,并返回平均结果。

完成下面的函数,计算老虎机每场比赛的平均值。

python
def estimate_average_slot_payout(n_runs):
    """Run the slot machine n_runs times and return the average net profit per run.
    Example calls (note that return value is nondeterministic!):
    >>> estimate_average_slot_payout(1)
    -1
    >>> estimate_average_slot_payout(1)
    0.5
    """
    pay_out = [play_slot_machine()-1 for i in range(n_runs)]
    return sum(pay_out)/n_runs
    pass